package com.schrodinger.zwebservice.axis2.client.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.QName;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

public class WebserviceDom4jUtils {
	public static final String SEPARATOR = "/";

	/**
	 * 取系统类路径
	 * 
	 * @return
	 */
	public static String getClassPath() {
		return WebserviceDom4jUtils.class.getClassLoader().getResource("").getPath();
	}

	/**
	 * 把路径中的“\\”替换成"/"
	 * 
	 * @param path
	 * @return
	 */
	public static String changePath(String path) {
		if (null == path || "".equals(path)) {
			return "";
		}
		if (path.indexOf("\\") != -1) {
			return path.replaceAll("\\\\", WebserviceDom4jUtils.SEPARATOR);
		} else {
			return path;
		}
	}

	/**
	 * 根据xml文件类路径取Document对象
	 * 
	 * @param xmlFilePath
	 *            xml文件类路径,如com/toone/a.xml
	 * @return
	 */
	public static Document getDocument(String xmlFilePath) {
		SAXReader reader = new SAXReader();
		Document doc = null;
		xmlFilePath = changePath(getClassPath() + xmlFilePath);
		try {
			InputStream input = new FileInputStream(xmlFilePath);
			doc = reader.read(input);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return doc;
	}

	/**
	 * 根据xml文件绝对路径取Document对象
	 * 
	 * @param xmlFilePath
	 *            xml文件类路径,如d:/com/toone/a.xml
	 * @return
	 */
	public static Document getDocumentByAbsPath(String xmlFilePath) {
		SAXReader reader = new SAXReader();
		Document doc = null;
		try {
			InputStream input = new FileInputStream(new File(xmlFilePath));
			doc = reader.read(input);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return doc;
	}

	/**
	 * 分析出XML Document对象
	 * 
	 * @param xmlDocumentStr
	 * @return
	 * @throws Exception
	 */
	public static Document parse(String xmlDocumentStr) throws Exception {
		try {
			SAXReader reader = new SAXReader();
			StringReader stringReader = new StringReader(xmlDocumentStr);
			Document document = reader.read(stringReader);
			stringReader.close();
			return document;
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("分析XML字符串失败", null);
		}
	}

	/**
	 * 分析出XML Document对象
	 * 
	 * @param xmldocumentstr
	 * @return
	 * @throws Exception
	 */
	public static Document parseFile(String xmlFilePath) throws Exception {
		try {
			SAXReader reader = new SAXReader();
			FileReader fileReader = new FileReader(xmlFilePath);
			Document document = reader.read(fileReader);
			fileReader.close();
			return document;
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("分析XML字符串失败", null);
		}
	}

	/**
	 * 分析出XML Document对象 输入XML片段
	 * 
	 * @param xmlDocumentStr
	 *            输入的XML片段，允许为空
	 * @return
	 * @throws Exception
	 */
	public static Document parseFragment(String xmlDocumentStr)
			throws Exception {
		try {
			if (xmlDocumentStr != null) {
				return DocumentHelper.parseText(xmlDocumentStr);
			} else {
				return DocumentHelper.createDocument();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("分析XML字符串失败", null);
		}
	}

	/**
	 * 文档串行化
	 * 
	 * @param document
	 * @return
	 * @throws Exception
	 */
	public static String toString(Document document) throws Exception {
		return document == null ? null : toString(document, null, -1);
	}

	/**
	 * 文档串行化，带参数
	 * 
	 * @author lujx
	 * @since Aug 31, 2007
	 * @param document
	 *            XML文档对象
	 * @param encoding
	 *            编码集。如"gbk","UTF-8"
	 * @param indentSize
	 *            换行之后子行和父行之间的缩进。若为0则换行，但是换行不缩进。为-1则不换行
	 * @return
	 * @throws Exception
	 */
	public static String toString(Document document, String encoding,
			int indentSize) throws Exception {
		try {
			OutputFormat format = new OutputFormat();
			if (encoding != null) {
				format.setEncoding(encoding);
			}
			if (indentSize >= 0) {// 换行
				format.setNewlines(true);
				if (indentSize != 0) {// 缩进
					format.setIndent(true);
					format.setIndentSize(indentSize);
				}
			}

			StringWriter str_writer = new StringWriter();
			XMLWriter writer = new XMLWriter(str_writer, format);
			writer.write(document);
			writer.close();

			String restr = str_writer.toString();
			str_writer.close();

			return restr;
		} catch (Exception e) {
			e.printStackTrace();
			throw new Exception("串行化文档失败", null);
		}
	}

	/**
	 * 根据标签名获取子元素，可以取多个元素
	 * 
	 * @param element
	 *            父元素
	 * @param tagName
	 *            标签名
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static List<Element> getSubElementByTagName(Element element, String tagName) {
		// 不指定标签名称则返回null
		if (tagName == null || "".equals(tagName.trim())) {
			return null;
		}

		List<Element> nodes = element
				.elements(new QName(tagName, element.getNamespace()));
		return null == nodes ? new ArrayList<Element>() : nodes;
	}

	/**
	 * 根据标签名获取子元素，适合只有一个子元素的情况
	 * 
	 * @author lujx
	 * @since Sep 3, 2007
	 * @param element
	 *            父元素
	 * @param tagName
	 *            标签名
	 */
	@SuppressWarnings("unchecked")
	public static Element getSingleElementByTagName(Element element,
			String tagName) {
		// 不指定标签名称则返回null
		if (tagName == null || "".equals(tagName.trim())) {
			return null;
		}

		List<Element> nodes = element
				.elements(new QName(tagName, element.getNamespace()));

		if (nodes == null || nodes.isEmpty()) {
			return null;
		} else {
			return (Element) nodes.get(0);
		}
	}

	/**
	 * 根据父元素和子标签名获取该子标签的内容，如果没有则返回空字符串，适合只有一个子元素的情况
	 * 
	 * @author lujx
	 * @since Sep 3, 2007
	 * @param element
	 *            父元素
	 * @param tagName
	 *            标签名
	 */
	public static String getContentByTagName(Element element, String tagName) {
		// 不指定标签名称则返回null
		if (tagName == null || "".equals(tagName.trim())) {
			return null;
		}
		return getSingleElementByTagName(element, tagName).getText();
	}

	/**
	 * 获取该标签的内容，如果没有则返回空字符串
	 * 
	 * @author lujx
	 * @since Oct 4, 2008
	 * @param element
	 *            该元素
	 */
	public static String getContent(Element element) {
		return element.getText();
	}

	/**
	 * 获取标签里对应属性的值
	 * 
	 * @author lujx
	 * @since Sep 3, 2007
	 * @param element
	 *            该标签
	 * @param attName
	 *            属性名
	 * @return 该属性的值
	 */
	public static String getValueByAttName(Element element, String attName) {
		// 不指定属性名称则返回null
		if (attName == null || "".equals(attName.trim())) {
			return null;
		}
		return element.valueOf("@" + attName);
	}

	/**
	 * 修改标签里对应属性的值，如果原来就有该属性则覆盖，如果原来没有该属性则添加
	 * 
	 * @author lujx
	 * @since May 19, 2008
	 * @param element
	 *            该标签
	 * @param attName
	 *            属性名
	 * @param attName
	 *            属性值
	 */
	public static void setValueByAttName(Element element, String attName,
			String attValue) {
		// 不指定属性名称则返回null
		if (attName == null || "".equals(attName.trim())) {
			return;
		}

		attValue = attValue == null ? "" : attValue;
		Attribute attribute = element.attribute(attName);// 获取属性对象
		if (attribute != null) {
			attribute.setValue(attValue);
		} else {
			element.addAttribute(attName, attValue);// 构造新属性
		}
	}

	/**
	 * 功能：获取指定父节点其下的所有直接子节点。和getSubElementByTagName不同之处在于其不指定儿子的名字
	 * 
	 * @author lujx
	 * @since Jun 23, 2008
	 * @param element
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static List<Element> getSubElementByParent(Element element) {
		return element.elements();
	}

	/**
	 * 功能：获取指定父节点下的所有子节点中符合条件的第一个节点（条件:指定属性为指定值）
	 * <p>
	 * by lujx 2009-2-5
	 * 
	 * @param element
	 *            父节点
	 * @param attName
	 *            属性名称
	 * @param attValue
	 *            属性值
	 * @return 如果找不到合适的则返回null
	 * @throws Exception
	 */
	public static Element getSubElementByValue(Element element, String attName,
			String attValue) throws Exception {
		// 不指定属性名称则返回null
		if (attName == null || "".equals(attName.trim())) {
			return null;
		}
		// 不指定属性值也返回null，允许指定属性值为空字符串
		if (attValue == null) {
			return null;
		}

		// 遍历所有子节点，发现符合条件的立即返回
		for (Iterator<Element> itr = getSubElementByParent(element).iterator(); itr
				.hasNext();) {
			Element subEle = (Element) itr.next();
			if (attValue.equals(getValueByAttName(subEle, attName))) {
				return subEle;
			}
		}
		return null;
	}

	/**
	 * 功能：往指定父节点下增加指定的节点
	 * <p>
	 * by lujx 2009-2-5
	 * 
	 * @param parent
	 *            父节点
	 * @param ele
	 *            子节点
	 * @throws Exception
	 */
	public static void addNode(Element parent, Element ele) throws Exception {
		if (parent != null && ele != null) {
			parent.add(ele);
		}
	}

	/**
	 * 功能：将某个节点对象的所有属性的值赋予目标对象的同名的属性上，类似于BeanUtil的copyProperties
	 * <P>
	 * by lujx 2009-2-19
	 * 
	 * @param dest
	 *            目标对象
	 * @param element
	 *            节点对象
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static void copyNodeToObject(Object dest, Element element)
			throws Exception {
		if (dest == null || element == null) {
			return;
		}

		for (Iterator<Attribute> itr = element.attributes().iterator(); itr.hasNext();) {
			Attribute attr = (Attribute) itr.next();
			BeanUtils.setProperty(dest, attr.getName(), attr.getValue());
		}
	}

	/**
	 * 功能：删除指定父节点下的指定的节点
	 * <p>
	 * by lujx 2009-2-20
	 * 
	 * @param parent
	 *            父节点
	 * @param ele
	 *            需要删除的子节点
	 * @throws Exception
	 */
	public static void deleteNode(Element parent, Element ele) throws Exception {
		if (parent != null && ele != null) {
			parent.remove(ele);
		}
	}

	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		try {
			Document doc = parseFile("c:\\c.xml");
			Element root = doc.getRootElement();
			Element node = getSingleElementByTagName(root, "meta");
			for (Iterator<Attribute> itr = node.attributes().iterator(); itr.hasNext();) {
				Attribute attr = (Attribute) itr.next();
				System.out.println(attr.getValue());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
